home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Newswatcher 2.0b22 / NW Source / Source / net.c < prev    next >
Encoding:
Text File  |  1994-10-11  |  55.0 KB  |  2,048 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     net.c
  4.  
  5.     This reusable module handles low-level communications with TCP/IP
  6.     network servers, using a simple "net ASCII" command/response 
  7.     stream model. It also exports several functions for using FTP data streams,
  8.     and several functions for doing domain name resolver tasks. The module
  9.     hides the complexity and peculiarities of the MacTCP device driver.
  10.     
  11.     The module also manages many of the common protocol details of communicating
  12.     with net ASCII servers, including all of the following: 
  13.     
  14.     Handling initial server "hello" messages.
  15.     Handling final server "QUIT" commands.
  16.     Mapping between CR line terminators and CRLF line terminators.
  17.     Decoding numeric server response codes.
  18.     Skipping server comments (response lines with "-" following the response code).
  19.     Supplying terminating "." lines when sending blocks of text.
  20.     Recognizing and discarding terminating "." lines when reading blocks of text.
  21.     Mapping between leading "." characters and leading ".." characters on text lines. 
  22.     
  23.     The module emphasizes simplicity at the expense of power. There are many things 
  24.     you can do by calling MacTCP directly which you cannot do with this module. 
  25.     The module is *not* a general purpose interface to MacTCP designed to meet the 
  26.     needs of all possible Mac TCP/IP networking programs. It is only a convenient 
  27.     interface for writing clients for typical simple net ASCII servers.
  28.     
  29.     The following mandatory functions handle initialization, idle time, and
  30.     termination tasks.
  31.     
  32.         NetInit - Initialize the module.
  33.         NetIdle - Handle idle time tasks.
  34.         NetTerm - Terminate the module.
  35.     
  36.     The following functions work with Net ASCII command/response streams.
  37.  
  38.         NetOpen - Open a stream.
  39.         NetClose - Close a stream.
  40.         NetCommand - Send a command and get the response.
  41.         NetGetExtraResponse - Get an extra command response.
  42.         NetBatchedCommands - Send several commands in a batch and
  43.             get and process the responses.
  44.         NetSendText - Send command text.
  45.         NetGetText - Get response text.
  46.         NetGetTextWithTruncation - Get response text with possible truncation.
  47.         NetGetChunkyText - Get response text in chunks.
  48.         
  49.     The following functions work with FTP data streams. They are used by 
  50.     the ftp.c module.
  51.     
  52.         NetFTPDataOpen - Open an FTP data stream.
  53.         NetFTPDataClose - Close an FTP data stream.
  54.         NetFTPDataWaitForConnection - Wait for the FTP server to open
  55.             its end of the FTP data stream.
  56.         NetGetFTPTextFile - Get a text file.
  57.         NetSendFTPTextFile - Send a text file.
  58.         
  59.     The following functions handle simple domain name resolver and related tasks.
  60.     
  61.         NetGetMyAddr - Get my IP address.
  62.         NetGetMyAddrStr - Get my IP address as a dotted-decimal string.
  63.         NetGetMyName - Get my domain name.
  64.         NetNameToAddr - Convert a domain name to an IP address.
  65.         NetAddrToName - Convert an IP address to a domain name.
  66.     
  67.     The following function can be used by client modules to give time to background
  68.     processes and check for user cancel operations.
  69.     
  70.         NetGiveTime - Give time to other processes.
  71.         
  72.     The following function can be used by the GiveTime function to determine whether
  73.     or not a DNR operation is in progress:
  74.     
  75.         NetDNROperationInProgress - Determine if a DNR operation is in progress.
  76.     
  77.     A "stream" is an abstraction representing a bidirectional network connection
  78.     to a TCP/IP server. The notion of a "stream" in this module combines the 
  79.     separate MacTCP notions of "stream" and "connection". When calling MacTCP 
  80.     directly, you "create" and "release" streams and "open" and "close" connections. 
  81.     These operations are combined in the net.c module. The "NetOpen" function both
  82.     creates a stream and opens a connection. The "NetClose" function both
  83.     closes the connection and releases the stream.
  84.     
  85.     A stream is represented as a handle of type "NetStreamHandle". These stream
  86.     handles are opaque. You may copy them and pass them as parameters to 
  87.     functions in net.c, but you are prohibited from accessing the contents of
  88.     the memory blocks pointed to by the stream handles. Only the functions in 
  89.     net.c are permitted to manipulate the contents of these blocks.
  90.     
  91.     All of the functions exported by this module return an OSErr error code
  92.     as the function result. This error code may have any of the following 
  93.     values:
  94.     
  95.         noErr                if no error occurred.
  96.         userCanceledErr        if the user canceled the operation.
  97.         other                any other OS, Toolbox, or MacTCP error code.
  98.         
  99.     In all of the functions which perform operations on streams, if the
  100.     user cancels the operation or some other error occurs, the stream
  101.     is aborted before returning to the caller. "Aborted" means that the
  102.     server connection is closed abruptly, without going through the usual TCP 
  103.     stream teardown process. The stream is also deallocated. In this case, you 
  104.     must not attempt to reuse the stream handle, since the stream no 
  105.     longer exists. You must perform careful error checking.
  106.     
  107.     Copyright © 1994, Northwestern University.
  108.  
  109. ----------------------------------------------------------------------------*/
  110.  
  111. #include <stdlib.h>
  112. #include <string.h>
  113. #include <ctype.h>
  114.  
  115. #include "MacTCPCommonTypes.h"
  116. #include "TCPPB.h"
  117. #include "AddressXlation.h"
  118. #include "GetMyIPAddr.h"
  119.  
  120. #include "def.h"
  121. #include "net.h"
  122. #include "memutil.h"
  123. #include "strutil.h"
  124.  
  125.  
  126.  
  127. /*    Constants. */
  128.  
  129. #define kMacTCPBufSize        16000            /* MacTCP stream buffer size */
  130. #define kInputBufSize         4000            /* input buffer size */
  131. #define kInputBufSizeDiv2    (kInputBufSize/2)
  132.  
  133.  
  134.  
  135. /*    Types. */
  136.  
  137. typedef struct TStream {
  138.     TCPiopb pBlock;                    /* MacTCP parameter block (must be first) */
  139.     unsigned long ipAddr;            /* IP address of host */
  140.     StreamPtr tcpStream;            /* MacTCP stream pointer */
  141.     struct wdsEntry wds[3];            /* MacTCP write data structure */
  142.     char *in;                        /* pointer to next location in input buffer in
  143.                                        which incoming network bytes should be stored */
  144.     char *out;                        /* pointer to next location in input buffer from
  145.                                        which bytes should be removed and delivered
  146.                                        to our clients (note: we always have out <= in) */
  147.     long myA5;                        /* our A5 register */
  148.     Boolean finished;                /* true when asynch close finished */
  149.     struct TStream **next;            /* handle to next asynch stream close queue element */
  150.     char mactcpBuf[kMacTCPBufSize];    /* MacTCP buffer */
  151.     char inputBuf[kInputBufSize];    /* input buffer */
  152.     Boolean fromFreeList;            /* true if storage obtained from free list */
  153.     struct TStream **nextFree;        /* handle to next available free buffer */
  154. } TStream, *TStreamPtr, **TStreamHandle;
  155.  
  156.  
  157.  
  158. /*    Global variables. */
  159.  
  160. static TStreamHandle gFreeStreamStorage = nil;    /* handle to first free buffer */
  161. static NetGiveTimeFunction gGiveTime;    /* pointer to GiveTime function */
  162. static NetLogFunction gLog;                /* pointer to logging function */
  163. static short gRefNum;                    /* MacTCP driver reference number */
  164. static TStreamHandle gCloseQueue = nil;    /* handle to queue of asynch closing streams */
  165.  
  166. static ResultUPP gDNRResultProcUPP = nil;
  167. static TCPIOCompletionUPP gCloseStreamCompletionRoutineUPP = nil;
  168.  
  169. static Boolean gDNROperationInProgress = false;
  170.  
  171.  
  172.  
  173. /*----------------------------------------------------------------------------
  174.     NewStreamBuffer 
  175.     
  176.     Allocate a stream buffer.
  177.     
  178.     Exit:    function result = error code.
  179.             *sh = handle to new stream buffer.
  180. ----------------------------------------------------------------------------*/
  181.  
  182. static OSErr NewStreamBuffer (TStreamHandle *sh)
  183. {
  184.     OSErr err = noErr;
  185.  
  186.     if (gFreeStreamStorage == nil) {
  187.         err = MyNewHandle(sizeof(TStream), sh);
  188.         if (err != noErr) return err;
  189.         MyHLockHi(*sh);
  190.         (***sh).fromFreeList = false;
  191.     } else {
  192.         *sh = gFreeStreamStorage;
  193.         gFreeStreamStorage = (**gFreeStreamStorage).nextFree;
  194.         memset(**sh, 0, sizeof(TStream));
  195.         (***sh).fromFreeList = true;
  196.     }
  197.     return noErr;
  198. }
  199.  
  200.  
  201.  
  202. /*----------------------------------------------------------------------------
  203.     DisposeStreamBuffer 
  204.     
  205.     Dispose a stream buffer.
  206.     
  207.     Entry:    sh = handle to stream buffer.
  208. ----------------------------------------------------------------------------*/
  209.  
  210. static void DisposeStreamBuffer (TStreamHandle sh)
  211. {
  212.     if ((**sh).fromFreeList) {
  213.         (**sh).nextFree = gFreeStreamStorage;
  214.         gFreeStreamStorage = sh;
  215.     } else {
  216.         MyDisposeHandle(sh);
  217.     }
  218. }
  219.  
  220.  
  221.  
  222. /*----------------------------------------------------------------------------
  223.     InitTCPBlock 
  224.     
  225.     Initialize a MacTCP parameter block.
  226.     
  227.     Entry:    pBlock = pointer to parameter block.
  228.             csCode = MacTCP control code (TCPCreate, etc.).
  229. ----------------------------------------------------------------------------*/
  230.  
  231. static void InitTCPBlock (TCPiopb *pBlock, short csCode)
  232. {
  233.     memset(pBlock, 0, sizeof(TCPiopb));
  234.     pBlock->ioResult = 1;
  235.     pBlock->ioCRefNum = gRefNum;
  236.     pBlock->csCode = csCode;
  237. }
  238.  
  239.  
  240.  
  241. /*----------------------------------------------------------------------------
  242.     CallMacTCP 
  243.     
  244.     Call MacTCP.
  245.     
  246.     Entry:    s = pointer to stream.
  247.             returnImmediatelyOnCancel = true to return immediately after
  248.                 a user cancel operation.
  249.     
  250.     Exit:    function result = system error result code.
  251. ----------------------------------------------------------------------------*/
  252.  
  253. static OSErr CallMacTCP (TStreamPtr s, Boolean returnImmediatelyOnCancel)
  254. {
  255.     OSErr err = noErr;
  256.  
  257.     PBControlAsync((ParmBlkPtr)&s->pBlock);
  258.     do {
  259.         err = (*gGiveTime)(true);
  260.         if (err != noErr && returnImmediatelyOnCancel) return err;
  261.     } while (s->pBlock.ioResult > 0);
  262.     if (err == noErr) err = s->pBlock.ioResult;
  263.     return err;
  264. }
  265.  
  266.  
  267.  
  268. /*----------------------------------------------------------------------------
  269.     DoTCPCreate 
  270.     
  271.     Create a MacTCP stream.
  272.     
  273.     Entry:    sh = handle to stream.
  274.     
  275.     Exit:    function result = system error result code.
  276. ----------------------------------------------------------------------------*/
  277.  
  278. static OSErr DoTCPCreate (TStreamHandle sh)
  279. {
  280.     TStreamPtr s;
  281.     OSErr err = noErr;
  282.  
  283.     s = (TStreamPtr)StripAddress(*sh);
  284.     InitTCPBlock(&s->pBlock, TCPCreate);
  285.     s->pBlock.csParam.create.rcvBuff = s->mactcpBuf;
  286.     s->pBlock.csParam.create.rcvBuffLen = kMacTCPBufSize;
  287.     err = CallMacTCP(s, false);
  288.     s->tcpStream = s->pBlock.tcpStream;
  289.     return err;
  290. }
  291.  
  292.  
  293.  
  294. /*----------------------------------------------------------------------------
  295.     DoTCPRelease 
  296.     
  297.     Release a MacTCP stream.
  298.     
  299.     Entry:    sh = handle to stream.
  300.     
  301.     Exit:    function result = system error result code.
  302.     
  303.     Note: Any active connection is also aborted, if necessary, before 
  304.     releasing the stream.
  305.     
  306.     Note: This function uses its own MacTCP parameter block instead of the
  307.     one inside the stream block, because the one inside the stream block
  308.     may already be in use by some other asynchronous operation.
  309. ----------------------------------------------------------------------------*/
  310.  
  311. static OSErr DoTCPRelease (TStreamHandle sh)
  312. {
  313.     TStreamPtr s;
  314.     TCPiopb pBlock;
  315.     OSErr err = noErr;
  316.  
  317.     s = (TStreamPtr)StripAddress(*sh);
  318.     InitTCPBlock(&pBlock, TCPRelease);
  319.     pBlock.tcpStream = s->tcpStream;
  320.     PBControlAsync((ParmBlkPtr)&pBlock);
  321.     while (pBlock.ioResult > 0) err = (*gGiveTime)(true);
  322.     if (err == noErr) err = pBlock.ioResult;
  323.     return err;
  324. }
  325.  
  326.  
  327.  
  328. /*----------------------------------------------------------------------------
  329.     DoTCPActiveOpen 
  330.     
  331.     Open an active MacTCP connection to a server.
  332.     
  333.     Entry:    sh = handle to stream.
  334.             addr = IP address of server.
  335.             port = port number of service.
  336.     
  337.     Exit:    function result = system error result code.
  338. ----------------------------------------------------------------------------*/
  339.  
  340. static OSErr DoTCPActiveOpen (TStreamHandle sh, unsigned long addr, 
  341.     unsigned short port)
  342. {
  343.     TStreamPtr s;
  344.  
  345.     s = (TStreamPtr)StripAddress(*sh);
  346.     InitTCPBlock(&s->pBlock, TCPActiveOpen);
  347.     s->pBlock.tcpStream = s->tcpStream;
  348.     s->pBlock.csParam.open.remoteHost = addr;
  349.     s->pBlock.csParam.open.remotePort = port;
  350.     return CallMacTCP(s, true);
  351. }
  352.  
  353.  
  354.  
  355. /*----------------------------------------------------------------------------
  356.     DoTCPPassiveOpen 
  357.     
  358.     Open a passive MacTCP connection.
  359.     
  360.     Entry:    sh = handle to stream.
  361.     
  362.     Exit:    function result = system error result code.
  363.             *port = assigned unused local port number.
  364.             
  365.     Note: Unlike the other "DoTCPxxx" functions, DoTCPPassiveOpen is
  366.     asynchronous. The passive stream is opened, but the function does not
  367.     wait for another host to connect. The caller is responsible for doing
  368.     this by polling s->pBlock.ioResult and waiting for it to become <= 0.
  369.     The function does, however, wait for the local port number to be assigned,
  370.     and it returns this local port number to the caller in the "port" parameter.
  371. ----------------------------------------------------------------------------*/
  372.  
  373. static OSErr DoTCPPassiveOpen (TStreamHandle sh, unsigned short *port)
  374. {
  375.     TStreamPtr s;
  376.     OSErr err = noErr;
  377.  
  378.     s = (TStreamPtr)StripAddress(*sh);
  379.     InitTCPBlock(&s->pBlock, TCPPassiveOpen);
  380.     s->pBlock.tcpStream = s->tcpStream;
  381.     PBControlAsync((ParmBlkPtr)&s->pBlock);
  382.     while (s->pBlock.csParam.open.localPort == 0) {
  383.         err = (*gGiveTime)(true);
  384.         if (err != noErr) return err;
  385.     }
  386.     *port = s->pBlock.csParam.open.localPort;
  387.     return noErr;
  388. }
  389.  
  390.  
  391.  
  392. /*----------------------------------------------------------------------------
  393.     DoTCPSend 
  394.     
  395.     Send data on a MacTCP stream.
  396.     
  397.     Entry:    sh = handle to stream.
  398.             (**sh).wds = MacTCP write data structure initialized to send
  399.                 the data.
  400.     
  401.     Exit:    function result = system error result code.
  402. ----------------------------------------------------------------------------*/
  403.  
  404. static OSErr DoTCPSend (TStreamHandle sh)
  405. {
  406.     TStreamPtr s;
  407.  
  408.     s = (TStreamPtr)StripAddress(*sh);
  409.     s->in = s->out = s->inputBuf;
  410.     InitTCPBlock(&s->pBlock, TCPSend);
  411.     s->pBlock.tcpStream = s->tcpStream;
  412.     s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
  413.     return CallMacTCP(s, true);
  414. }
  415.  
  416.  
  417.  
  418. /*----------------------------------------------------------------------------
  419.     DoTCPRcv 
  420.     
  421.     Reveive data on a MacTCP stream.
  422.     
  423.     Entry:    sh = handle to stream.
  424.             data = pointer to data buffer.
  425.             *len = length of data buffer.
  426.     
  427.     Exit:    function result = system error result code.
  428.             *len = number of bytes received.
  429. ----------------------------------------------------------------------------*/
  430.  
  431. static OSErr DoTCPRcv (TStreamHandle sh, Ptr data, unsigned short *len)
  432. {
  433.     TStreamPtr s;
  434.     OSErr err = noErr;
  435.     
  436.     s = (TStreamPtr)StripAddress(*sh);
  437.     InitTCPBlock(&s->pBlock, TCPRcv);
  438.     s->pBlock.tcpStream = s->tcpStream;
  439.     s->pBlock.csParam.receive.rcvBuff = StripAddress(data);
  440.     s->pBlock.csParam.receive.rcvBuffLen = *len;
  441.     err = CallMacTCP(s, true);
  442.     *len = s->pBlock.csParam.receive.rcvBuffLen;
  443.     return err;
  444. }
  445.  
  446.  
  447.  
  448. /*----------------------------------------------------------------------------
  449.     SendCommand 
  450.     
  451.     Send a command on a stream.
  452.     
  453.     Entry:    sh = handle to stream.
  454.             command = C-format command string.
  455.     
  456.     Exit:    function result = system error result code.
  457. ----------------------------------------------------------------------------*/
  458.  
  459. static OSErr SendCommand (TStreamHandle sh, char *command)
  460. {
  461.     TStreamPtr s;
  462.     OSErr err = noErr;
  463.  
  464.     s = (TStreamPtr)StripAddress(*sh);
  465.     s->wds[0].length = strlen(command);
  466.     s->wds[0].ptr = StripAddress(command);
  467.     s->wds[1].length = 2;
  468.     s->wds[1].ptr = CRLF;
  469.     s->wds[2].length = 0;
  470.     s->wds[2].ptr = nil;
  471.     err = DoTCPSend(sh);
  472.     if (err != noErr) return err;
  473.     if (gLog != nil) (*gLog)(true, s->ipAddr, command);
  474.     return noErr;
  475. }
  476.  
  477.  
  478.  
  479. /*----------------------------------------------------------------------------
  480.     ReadResponse 
  481.     
  482.     Read a command response from a stream.
  483.     
  484.     Entry:    sh = handle to stream.
  485.     
  486.     Exit:    function result = system error result code.
  487.             *responseCode = server response code.
  488.             response = C-format server response string, including the 
  489.                 response code.
  490.                 
  491.     Response comment lines are discarded (response lines with a
  492.     '-' immediately following the response code).
  493. ----------------------------------------------------------------------------*/
  494.  
  495. static OSErr ReadResponse (TStreamHandle sh, long *responseCode, CStr255 response)
  496. {
  497.     TStreamPtr s;
  498.     char *in, *out, *p, *q, *r;
  499.     unsigned short len, numUnread;
  500.     long num;
  501.     OSErr err = noErr;
  502.     
  503.     s = (TStreamPtr)StripAddress(*sh);
  504.     in = s->in;
  505.     out = s->out;
  506.     
  507.     while (true) {
  508.         while (out < in) {
  509.             for (p = out; p < in-1 && !(*p == CR && *(p+1) == LF); p++) /* do nothing */ ;
  510.             if (p >= in-1) break;
  511.             *p = 0;
  512.             q = out;
  513.             out = p+2;
  514.             r = q;
  515.             num = CrackNum(&r);
  516.             if (*r != '-' && num != 0) {
  517.                 if (p - q > 255) *(q+255) = 0;
  518.                 strcpy(response, q);
  519.                 *responseCode = num;
  520.                 s->in = in;
  521.                 s->out = out;
  522.                 if (gLog != nil) (*gLog)(false, s->ipAddr, response);
  523.                 return noErr;
  524.             }
  525.         }
  526.         if (out == in) {
  527.             out = in = s->inputBuf;
  528.         } else if (in > s->inputBuf + kInputBufSizeDiv2) {
  529.             numUnread = in - out;
  530.             if (numUnread > kInputBufSizeDiv2) {
  531.                 numUnread = kInputBufSizeDiv2;
  532.                 out = in - numUnread;
  533.             }
  534.             BlockMoveData(out, s->inputBuf, numUnread);
  535.             out = s->inputBuf;
  536.             in = out + numUnread;
  537.         }
  538.         len = s->inputBuf + kInputBufSize - in;
  539.         err = DoTCPRcv(sh, in, &len);
  540.         if (err != noErr) return err;
  541.         in += len;
  542.     }
  543. }
  544.  
  545.  
  546.  
  547. /*----------------------------------------------------------------------------
  548.     DNRResultProc 
  549.     
  550.     MacTCP domain name resolver completion routine.
  551.     
  552.     Entry:     hInfoPtr = pointer to hostInfo struct.
  553.             userDataPtr = pointer to user data.
  554. ----------------------------------------------------------------------------*/
  555.  
  556. static pascal void DNRResultProc (struct hostInfo *hInfoPtr, char *userDataPtr)
  557. {
  558.     *(Boolean*)userDataPtr = true;
  559. }
  560.  
  561.  
  562.  
  563. /*----------------------------------------------------------------------------
  564.     CloseStreamCompletionRoutine
  565.     
  566.     This is the asynchronous completion routine used to close streams.
  567.     
  568.     This completion routine chains to itself to do the following tasks
  569.     involved in gracefully tearing down a stream in the background:
  570.     
  571.     Wait for QUIT command send to complete (the send is initiated by the
  572.         NetClose function below).
  573.     Read incoming data until an error occurs (signalling that the
  574.         server has closed its end of the connection).
  575.     Close our end of the connection.
  576.     
  577.     Entry:    pBlock = pointer to MacTCP parameter block.
  578. ----------------------------------------------------------------------------*/
  579.  
  580. static void CloseStreamCompletionRoutine (TCPiopb *pBlock)
  581. {
  582.     TStreamPtr s;
  583.     OSErr err = noErr;
  584.     long savedA5;
  585.  
  586.     s = (TStreamPtr)pBlock;
  587.     
  588.     savedA5 = SetA5(s->myA5);
  589.     
  590.     if (s->pBlock.csCode == TCPSend) {
  591.     
  592.         /* The QUIT command has been sent. Start the first receive. */
  593.         
  594.         if (s->pBlock.ioResult != noErr) {
  595.             s->finished = true;
  596.             SetA5(savedA5);
  597.             return;
  598.         }
  599.         InitTCPBlock(&s->pBlock, TCPRcv);
  600.         s->pBlock.ioCompletion = 
  601.             (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
  602.         s->pBlock.tcpStream = s->tcpStream;
  603.         s->pBlock.csParam.receive.rcvBuff = s->inputBuf;
  604.         s->pBlock.csParam.receive.rcvBuffLen = kInputBufSize;
  605.     
  606.     } else if (s->pBlock.csCode == TCPRcv) {
  607.     
  608.         /* A receive operation has finished. If there was no error, start another
  609.            receive. If an error occurred, it was because the server has closed
  610.            its side of the connection. In this case, we start the TCPClose. */
  611.        
  612.            err = s->pBlock.ioResult;
  613.         InitTCPBlock(&s->pBlock, err == noErr ? TCPRcv : TCPClose);
  614.         s->pBlock.ioCompletion = 
  615.             (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
  616.         s->pBlock.tcpStream = s->tcpStream;
  617.         if (err == noErr) {
  618.             s->pBlock.csParam.receive.rcvBuff = s->inputBuf;
  619.             s->pBlock.csParam.receive.rcvBuffLen = kInputBufSize;
  620.         }
  621.     
  622.     } else if (s->pBlock.csCode == TCPClose) {
  623.     
  624.         /* The close has finished. Set the "finished" field to signal 
  625.            that we can now release the stream and dispose of the queue 
  626.            element. */
  627.  
  628.         s->finished = true;
  629.         SetA5(savedA5);
  630.         return;
  631.                     
  632.     }
  633.     
  634.     /* Issue the next PBControl call in the chain. */
  635.  
  636.     s->pBlock.ioResult = 1;
  637.     err = PBControlAsync((ParmBlkPtr)&s->pBlock);
  638.     if (err != noErr) s->finished = true;
  639.     SetA5(savedA5);
  640.     
  641. }
  642.  
  643.  
  644.  
  645. /*----------------------------------------------------------------------------
  646.     MungeOut 
  647.     
  648.     Munge a block of text before sending it out to a server.
  649.     
  650.     Entry:    text = handle to CR-terminated ASCII text lines.
  651.                 Warning: the memory block is modified by the function.
  652.                 The memory block must be unlocked and nonpurgeable.
  653.             mungePeriods = true to munge periods.
  654.     
  655.     Exit:    function result = system error result code.
  656.     
  657.     CR line terminators are translated to CRLF. If the text block does
  658.     not have a terminating CR at the end of the last line, one is added.
  659.     
  660.     If mungePeriods=true, leading "." characters on lines are mapped to 
  661.     "..", and a terminating "." on a line by itself is added to the end
  662.     of the text block.
  663. ----------------------------------------------------------------------------*/
  664.  
  665. static OSErr MungeOut (Handle text, Boolean mungePeriods)
  666. {
  667.     OSErr err = noErr;
  668.     char *p, *pEnd, *pStart, *q;
  669.     long textLen, oldTextLen;
  670.  
  671.     textLen = MyGetHandleSize(text);
  672.     p = *text;
  673.     pEnd = p + textLen;
  674.     if (textLen == 0 || *(pEnd-1) != CR) {
  675.         textLen++;
  676.         err = MySetHandleSize(text, textLen);
  677.         if (err != noErr) return err;
  678.         p = *text;
  679.         pEnd = p + textLen;
  680.         *(pEnd-1) = CR;
  681.     }
  682.     oldTextLen = textLen;
  683.     if (mungePeriods) {
  684.         while (p < pEnd) {
  685.             if (*p == '.') textLen++;
  686.             while (*p != CR) p++;
  687.             p++;
  688.             textLen++;
  689.         }
  690.         textLen += 3;
  691.     } else {
  692.         for (; p < pEnd; p++) if (*p == CR) textLen++;
  693.     }
  694.     err = MySetHandleSize(text, textLen);
  695.     if (err != noErr) return err;
  696.     pStart = *text;
  697.     p = pStart + oldTextLen - 1;
  698.     q = pStart + textLen - 1;
  699.     if (mungePeriods) {
  700.         *q-- = LF;
  701.         *q-- = CR;
  702.         *q-- = '.';
  703.         while (p >= pStart) {
  704.             *q-- = LF;
  705.             *q-- = CR;
  706.             p--;
  707.             while (*p != CR && p >= pStart) *q-- = *p--;
  708.             if (*(p+1) == '.') *q-- = '.';
  709.         }
  710.     } else {
  711.         while (p >= pStart) {
  712.             if (*p == CR) *q-- = LF;
  713.             *q-- = *p--;
  714.         }
  715.     }
  716.     return noErr;
  717. }
  718.  
  719.  
  720.  
  721. /*----------------------------------------------------------------------------
  722.     MungeIn 
  723.     
  724.     Munge a block of text after receiving it from a server.
  725.     
  726.     Entry:    text = handle to ASCII text lines as received from server.
  727.                 Warning: the memory block is modified by the function.
  728.                 The memory block must be unlocked and nonpurgeable.
  729.             textLen = number of characters in text block.
  730.             mungePeriods = true to munge periods.
  731.     
  732.     Exit:    function result = system error result code.
  733.     
  734.     CRLF line terminators are translated to CR. A final CR is added at the
  735.     end of the last line if necessary.
  736.     
  737.     If mungePeriods=true, leading ".." characters on lines are mapped to 
  738.     ".", and the terminating "." on a line by itself is removed from the end
  739.     of the text block.
  740. ----------------------------------------------------------------------------*/
  741.  
  742. static OSErr MungeIn (Handle text, long textLen, Boolean mungePeriods)
  743. {
  744.     OSErr err = noErr;
  745.     char *pEnd, *p, *q;
  746.     Boolean needTerminatingCR = false;
  747.  
  748.     if (mungePeriods && textLen >= 3) {
  749.         p = *text + textLen - 3;
  750.         if (*p == '.' && *(p+1) == CR && *(p+2) == LF) textLen -= 3;
  751.     }
  752.     p = q = *text;
  753.     pEnd = p + textLen;
  754.     if (mungePeriods) {
  755.         while (p < pEnd) {
  756.             if (*p == '.' && *(p+1) == '.') {
  757.                 *q++ = '.';
  758.                 p += 2;
  759.             }
  760.             while (*p != CR || *(p+1) != LF) *q++ = *p++;
  761.             *q++ = CR;
  762.             p += 2;
  763.         }
  764.     } else {
  765.         while (p < pEnd) {
  766.             if (*p == CR && *(p+1) == LF) {
  767.                 *q++ = CR;
  768.                 p += 2;
  769.             } else {
  770.                 *q++ = *p++;
  771.             }
  772.         }
  773.     }
  774.     textLen = q - *text;
  775.     if (textLen > 0 && *(q-1) != CR) {
  776.         textLen++;
  777.         needTerminatingCR = true;
  778.     }
  779.     err = MySetHandleSize(text, textLen);
  780.     if (err != noErr) return err;
  781.     if (needTerminatingCR) *(*text + textLen - 1) = CR;
  782.     return noErr;
  783. }
  784.  
  785.  
  786.  
  787. /*----------------------------------------------------------------------------
  788.     GetText 
  789.     
  790.     Get response text from a server.
  791.     
  792.     Entry:    stream = handle to stream.
  793.             truncateFunction = pointer to truncate function, or nil if none.
  794.     
  795.     Exit:    function result = system error result code.
  796.             *text = handle to received ASCII text lines. Each line is
  797.                 terminated with a CR.
  798.             *truncated = true if text truncated.
  799.             
  800.     CRLF line terminators are translated to CR. The server must send a 
  801.     terminating "." on a line by itself. This terminating line is not 
  802.     included in the "text" handle returned to the caller. Leading
  803.     ".." characters on lines are mapped to ".".
  804.     
  805.     The truncation function is called every time a new block of text is
  806.     received from the server. This function must be declared as follows:
  807.     
  808.     Boolean TruncateFunction (Handle t, long tLen, long *pos)
  809.     
  810.     Entry:    t = handle to raw text received from server so far.
  811.             tLen = length of text received from server so far.
  812.             *pos = saved position in text (0 on first call).
  813.             
  814.     Exit:    function result = true if text should be truncated.
  815.             *pos = updated saved position in text if no truncation,
  816.                 length of truncated text if truncated.
  817.                 
  818.     If the truncate function returns true, GetText aborts
  819.     the stream and returns to the caller immediately.
  820.     
  821.     The text passed to the truncate function is raw. It contains CRLF line
  822.     terminators and doubled leading ".." characters. The text returned to 
  823.     NetGetTextWithTruncation's caller, however, is processed as indicated
  824.     above.
  825. ----------------------------------------------------------------------------*/
  826.  
  827. static OSErr GetText (NetStreamHandle stream, NetTruncateFunction truncateFunction,
  828.     Handle *text, Boolean *truncated)
  829. {
  830.     TStreamHandle sh;
  831.     TStreamPtr s;
  832.     OSErr err = noErr;
  833.     Handle t = nil;
  834.     long tLen = 0;
  835.     long tAllocated = 0;
  836.     char *tEnd;
  837.     long numFree;
  838.     unsigned short len;
  839.     long pos = 0;
  840.     Boolean trunc = false;
  841.     
  842.     sh = (TStreamHandle)stream;
  843.     s = (TStreamPtr)StripAddress(*sh);
  844.     tLen = s->in - s->out;
  845.     tAllocated = tLen + 4000;
  846.     err = MyNewHandle(tAllocated, &t);
  847.     if (err != noErr) goto exit;
  848.     if (tLen > 0) BlockMoveData(s->out, *t, tLen);
  849.     s->in = s->out = s->inputBuf;
  850.     
  851.     while (true) {
  852.         if (truncateFunction != nil && (*truncateFunction)(t, tLen, &pos)) {
  853.             trunc = true;
  854.             tLen = pos;
  855.             break;
  856.         }
  857.         if (tLen >= 3) {
  858.             tEnd = *t + tLen;
  859.             if (*(tEnd-3) == '.' && *(tEnd-2) == CR && *(tEnd-1) == LF) {
  860.                 if (tLen == 3) break;
  861.                 if (tLen >= 5 && *(tEnd-5) == CR && *(tEnd-4) == LF) break;
  862.             }
  863.         }
  864.         numFree = tAllocated - tLen;
  865.         if (numFree <= 4000) {
  866.             tAllocated += 4000;
  867.             err = MySetHandleSize(t, tAllocated);
  868.             if (err != noErr) goto exit;
  869.         }
  870.         len = 4000;
  871.         MyHLock(t);
  872.         err = DoTCPRcv(sh, *t + tLen, &len);
  873.         if (err != noErr) goto exit;
  874.         MyHUnlock(t);
  875.         tLen += len;
  876.     }
  877.         
  878.     err = MungeIn(t, tLen, true);
  879.     if (err != noErr) goto exit;
  880.     
  881.     *text = t;
  882.     if (trunc) {
  883.         DoTCPRelease(sh);
  884.         DisposeStreamBuffer(sh);
  885.     }
  886.     *truncated = trunc;
  887.     return noErr;
  888.     
  889. exit:
  890.  
  891.     DoTCPRelease(sh);
  892.     DisposeStreamBuffer(sh);
  893.     MyDisposeHandle(t);
  894.     return err;
  895. }
  896.  
  897.  
  898.  
  899. /*----------------------------------------------------------------------------
  900.     NetInit 
  901.     
  902.     Initialize the net.c module. This function must be called before calling
  903.     any other functions in the module. It opens the MacTCP driver.
  904.     
  905.     Entry:    giveTime = pointer to give time function.
  906.             log = pointer to logging function, or nil if none.
  907.             numBuffs = number of stream buffers to preallocate and lock
  908.                 at the top of the heap.
  909.     
  910.     Exit:    function result = system error result code.
  911.     
  912.     The give time function is called repeatedly during all network
  913.     operations. It must be declared as follows:
  914.     
  915.         OSErr GiveTime (Boolean waiting)
  916.     
  917.     This function should do two things: give time to other processes by calling 
  918.     WaitNextEvent, and check for user cancel events (e.g., clicking on a Cancel 
  919.     button or pressing Command-period or Escape). The function must return 
  920.     userCanceledErr if the user has canceled the operation, noErr otherwise. If
  921.     waiting=true, the function should call WaitNextEvent to give time to other
  922.     processes continuously. If waiting=false, the function may throttle 
  923.     WaitNextEvent calls to every 4-6 ticks. The net.c module calls the function
  924.     with waiting=true.
  925.     
  926.     The logging function is called once for each server command and response.
  927.     It must be declared as follows:
  928.     
  929.         void Log (Boolean command, unsigned long ipAddr, char *str)
  930. ----------------------------------------------------------------------------*/
  931.  
  932. OSErr NetInit (NetGiveTimeFunction giveTime, NetLogFunction log, short numBuffs)
  933. {
  934.     short i;
  935.     TStreamHandle sh;
  936.     OSErr err = noErr;
  937.  
  938.     gGiveTime = giveTime;
  939.     gLog = log;
  940.     for (i = 0; i < numBuffs; i++) {
  941.         err = MyNewHandle(sizeof(TStream), &sh);
  942.         if (err != noErr) return err;
  943.         MyHLockHi(sh);
  944.         (**sh).nextFree = gFreeStreamStorage;
  945.         gFreeStreamStorage = sh;
  946.     }
  947.     return OpenDriver("\p.IPP", &gRefNum);
  948. }
  949.  
  950.  
  951.  
  952. /*----------------------------------------------------------------------------
  953.     NetIdle 
  954.     
  955.     Handle idle time tasks. You should call this function in your main 
  956.     event loop.
  957.     
  958.     Exit:    function result = system error result code (always noErr).
  959.     
  960.     This function takes care of releasing and deallocating streams which
  961.     have finished closing asynchronously in the background.
  962. ----------------------------------------------------------------------------*/
  963.  
  964. OSErr NetIdle (void)
  965. {
  966.     TStreamHandle sh, next, prev = nil;
  967.     TStreamPtr s;
  968.  
  969.     for (sh = gCloseQueue; sh != nil; sh = next) {
  970.         s = (TStreamPtr)StripAddress(*sh);
  971.         next = s->next;
  972.         if (s->finished) {
  973.             DoTCPRelease(sh);
  974.             if (prev == nil) {
  975.                 gCloseQueue = next;
  976.             } else {
  977.                 (**prev).next = next;
  978.             }
  979.             DisposeStreamBuffer(sh);
  980.         } else {
  981.             prev = sh;
  982.         }
  983.     }
  984.     return noErr;
  985. }
  986.  
  987.  
  988.  
  989. /*----------------------------------------------------------------------------
  990.     NetTerm 
  991.     
  992.     Terminate the module. You must call this function when you quit.
  993.     
  994.     Exit:    function result = system error result code (always noErr).
  995.     
  996.     This functions waits until all asynchronous stream close operations have
  997.     completed, or for 5 seconds, whichever happens first. If any close
  998.     operations are still pending after 5 seconds, they are aborted.
  999. ----------------------------------------------------------------------------*/
  1000.  
  1001. OSErr NetTerm (void)
  1002. {
  1003.     TStreamHandle sh;
  1004.     long waitTil;
  1005.  
  1006.     waitTil = TickCount() + 300;
  1007.     while (gCloseQueue != nil && TickCount() < waitTil) {
  1008.         (*gGiveTime)(true);
  1009.         NetIdle();
  1010.     }
  1011.     if (gCloseQueue != nil) {
  1012.         for (sh = gCloseQueue; sh != nil; sh = (**sh).next) (**sh).finished = true;
  1013.         NetIdle();
  1014.     }
  1015.     return noErr;
  1016. }
  1017.  
  1018.  
  1019.  
  1020. /*----------------------------------------------------------------------------
  1021.     NetOpen 
  1022.     
  1023.     Open a stream.
  1024.     
  1025.     Entry:    addr = IP address of server.
  1026.             port = port number of service.
  1027.     
  1028.     Exit:    function result = system error result code.
  1029.             *stream = handle to opened stream.
  1030.             *responseCode = server hello message response code.
  1031.             response = C-format hello message string, including the
  1032.                 response code.
  1033.                 
  1034.     Hello message comment lines are discarded (response lines with a
  1035.     '-' immediately following the response code).
  1036. ----------------------------------------------------------------------------*/
  1037.  
  1038. OSErr NetOpen (unsigned long addr, unsigned short port, NetStreamHandle *stream, 
  1039.     long *responseCode, CStr255 response)
  1040. {
  1041.     TStreamHandle sh;
  1042.     TStreamPtr s;
  1043.     OSErr err = noErr;
  1044.     
  1045.     err = NewStreamBuffer(&sh);
  1046.     if (err != noErr) return err;
  1047.     s = (TStreamPtr)StripAddress(*sh);
  1048.     s->in = s->out = s->inputBuf;
  1049.     s->ipAddr = addr;
  1050.     s->myA5 = SetCurrentA5();
  1051.     err = DoTCPCreate(sh);
  1052.     if (err != noErr) goto exit;
  1053.     err = DoTCPActiveOpen(sh, addr, port);
  1054.     if (err != noErr) goto exit;
  1055.     err = ReadResponse(sh, responseCode, response);
  1056.     if (err != noErr) goto exit;
  1057.     *stream = (NetStreamHandle)sh;
  1058.     return noErr;
  1059.     
  1060. exit:
  1061.  
  1062.     DoTCPRelease(sh);
  1063.     DisposeStreamBuffer(sh);
  1064.     if (err == connectionClosing || err == connectionDoesntExist ||
  1065.         err == connectionTerminated) err = openFailed;
  1066.     return err;
  1067. }
  1068.  
  1069.  
  1070.  
  1071. /*----------------------------------------------------------------------------
  1072.     NetClose 
  1073.     
  1074.     Close a stream.
  1075.     
  1076.     Entry:    stream = handle to stream.
  1077.     
  1078.     Exit:    function result = system error result code (always noErr).
  1079.     
  1080.     A QUIT command is sent to the server before closing the stream. 
  1081.     Thus you do not need to and should not send this command yourself.
  1082.     
  1083.     To improve performance, all streams are closed asynchronously using
  1084.     chained MacTCP device driver completion routines. The function returns
  1085.     immediately without any delay.
  1086.     
  1087.     This asynchronous stream closing feature also permits you to close 
  1088.     connections in the background without interfering with or delaying user 
  1089.     actions in the foreground.
  1090. ----------------------------------------------------------------------------*/
  1091.  
  1092. OSErr NetClose (NetStreamHandle stream)
  1093. {
  1094.     TStreamHandle sh;
  1095.     TStreamPtr s;
  1096.     OSErr err = noErr;
  1097.  
  1098.     sh = (TStreamHandle)stream;
  1099.     s = (TStreamPtr)StripAddress(*sh);
  1100.     
  1101.     /* Link the stream into the queue of closing streams. */
  1102.  
  1103.     s->myA5 = SetCurrentA5();
  1104.     s->finished = false;
  1105.     s->next = gCloseQueue;
  1106.     gCloseQueue = sh;
  1107.         
  1108.     /* Send an asynchronous "QUIT" command to the server, chained 
  1109.        to our completion routine . */
  1110.     
  1111.     s->wds[0].length = 6;
  1112.     s->wds[0].ptr = "QUIT\r\n";
  1113.     s->wds[1].length = 0;
  1114.     s->wds[1].ptr = nil;
  1115.     InitTCPBlock(&s->pBlock, TCPSend);
  1116.     s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
  1117.     s->pBlock.ioCompletion = 
  1118.         (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
  1119.     s->pBlock.tcpStream = s->tcpStream;
  1120.     err = PBControlAsync((ParmBlkPtr)&s->pBlock);
  1121.     if (err != noErr) s->finished = true;
  1122.     return noErr;
  1123. }
  1124.  
  1125.  
  1126.  
  1127. /*----------------------------------------------------------------------------
  1128.     NetCommand 
  1129.     
  1130.     Send a command to a server and get the response.
  1131.     
  1132.     Entry:    stream = handle to stream.
  1133.             command = C-format command string.
  1134.     
  1135.     Exit:    function result = system error result code.
  1136.             *responseCode = server response code.
  1137.             response = C-format server response string, including the 
  1138.                 response code.
  1139.                 
  1140.     Response comment lines are discarded (response lines with a
  1141.     '-' immediately following the response code).
  1142. ----------------------------------------------------------------------------*/
  1143.  
  1144. OSErr NetCommand (NetStreamHandle stream, char *command, 
  1145.     long *responseCode, CStr255 response)
  1146. {
  1147.     TStreamHandle sh;
  1148.     OSErr err = noErr;
  1149.  
  1150.     sh = (TStreamHandle)stream;
  1151.     err = SendCommand(sh, command);
  1152.     if (err != noErr) goto exit;
  1153.     err = ReadResponse(sh, responseCode, response);
  1154.     if (err != noErr) goto exit;
  1155.     return noErr;
  1156.     
  1157. exit:
  1158.  
  1159.     DoTCPRelease(sh);
  1160.     DisposeStreamBuffer(sh);
  1161.     return err;
  1162. }
  1163.  
  1164.  
  1165.  
  1166. /*----------------------------------------------------------------------------
  1167.     NetGetExtraResponse 
  1168.     
  1169.     Get an extra command response from a server.
  1170.     
  1171.     Entry:    stream = handle to stream.
  1172.     
  1173.     Exit:    function result = system error result code.
  1174.             *responseCode = server response code.
  1175.             response = C-format server response string, including the 
  1176.                 response code.
  1177.                 
  1178.     Response comment lines are discarded (response lines with a
  1179.     '-' immediately following the response code).
  1180.     
  1181.     Some servers have commands which return more than one response. The
  1182.     NetCommand function above reads the first response. This function is
  1183.     used to read the additional response(s). The FTP RETR and STOR commands
  1184.     are examples.
  1185. ----------------------------------------------------------------------------*/
  1186.  
  1187. OSErr NetGetExtraResponse (NetStreamHandle stream, long *responseCode, 
  1188.     CStr255 response)
  1189. {
  1190.     TStreamHandle sh;
  1191.     OSErr err = noErr;
  1192.  
  1193.     sh = (TStreamHandle)stream;
  1194.     err = ReadResponse(sh, responseCode, response);
  1195.     if (err != noErr) goto exit;
  1196.     return noErr;
  1197.     
  1198. exit:
  1199.  
  1200.     DoTCPRelease(sh);
  1201.     DisposeStreamBuffer(sh);
  1202.     return err;
  1203. }
  1204.  
  1205.  
  1206.  
  1207. /*----------------------------------------------------------------------------
  1208.     NetBatchedCommands 
  1209.     
  1210.     Send multiple batched commands to a server and get and process
  1211.     the responses.
  1212.     
  1213.     Entry:    stream = handle to stream.
  1214.             commands = handle to CR-terminated commands.
  1215.                 Warning: the memory block is modified by the function.
  1216.                 The memory block must be unlocked and nonpurgeable.
  1217.             doOneResponse = pointer to response processing function.
  1218.     
  1219.     Exit:    function result = system error result code.
  1220.     
  1221.     The response processing function must be declared as follows:
  1222.  
  1223.         void DoOneResponse (long responseCode, CStr255 response)
  1224.     
  1225.     The function is called once for each command response. It is passed the 
  1226.     following parameters:
  1227.     
  1228.         responseCode = server response code.
  1229.         response = C-format server response string, including the
  1230.             response code.
  1231.                 
  1232.     Response comment lines are discarded (leading response lines with a
  1233.     '-' immediately following the response code).
  1234.     
  1235.     CR line terminators are mapped to CRLF line terminators.
  1236.     
  1237.     When you know in advance that you need to send a number of commands to a
  1238.     server all in a row, it is usually much more efficient to send them in
  1239.     a batch rather than sending them one at a time using the NetCommand function
  1240.     above. You should take care, however, since not all servers support batched 
  1241.     commands. 
  1242.     
  1243.     All of the batched commands must return only a single response (after
  1244.     skipping any possible comment lines).
  1245. ----------------------------------------------------------------------------*/
  1246.  
  1247. OSErr NetBatchedCommands (NetStreamHandle stream, Handle commands, 
  1248.     NetDoOneResponse doOneResponse)
  1249. {
  1250.     TStreamHandle sh;
  1251.     TStreamPtr s;
  1252.     OSErr err = noErr;
  1253.     char *p, *q, *r;
  1254.     long cmdLen;
  1255.     unsigned short len;
  1256.     long responseCode;
  1257.     CStr255 response;
  1258.     short numCmds, i;
  1259.     char state;
  1260.     
  1261.     state = MyHGetState(commands);
  1262.  
  1263.     err = MungeOut(commands, false);
  1264.     if (err != noErr) goto exit;
  1265.     cmdLen = MyGetHandleSize(commands);
  1266.     
  1267.     sh = (TStreamHandle)stream;
  1268.     s = (TStreamPtr)StripAddress(*sh);
  1269.     s->wds[1].length = 0;
  1270.     s->wds[1].ptr = nil;
  1271.     MyHLock(commands);
  1272.     p = *commands;
  1273.     while (cmdLen > 0) {
  1274.         len = cmdLen > 4000 ? 4000 : cmdLen;
  1275.         q = p + len - 1;
  1276.         while (q > p && (*q != LF || *(q-1) != CR)) q--;
  1277.         if (q == p) {
  1278.             p += len;
  1279.             cmdLen -= len;
  1280.             continue;
  1281.         } else {
  1282.             numCmds = 0;
  1283.             len = q - p + 1;
  1284.             while (q > p) {
  1285.                 if (*q == LF && *(q-1) == CR) {
  1286.                     numCmds++;
  1287.                     q -= 2;
  1288.                 } else {
  1289.                     q--;
  1290.                 }
  1291.             }
  1292.         }
  1293.         s->wds[0].length = len;
  1294.         s->wds[0].ptr = StripAddress(p);
  1295.         err = DoTCPSend(sh);
  1296.         if (err != noErr) goto exit;
  1297.         if (gLog != nil) {
  1298.             q = p;
  1299.             for (i = 0; i < numCmds; i++) {
  1300.                 r = q;
  1301.                 while (*r != CR || *(r+1) != LF) r++;
  1302.                 *r = 0;
  1303.                 (*gLog)(true, s->ipAddr, q);
  1304.                 q = r + 2;
  1305.             }
  1306.         }
  1307.         p += len;
  1308.         cmdLen -= len;
  1309.         while (numCmds--) {
  1310.             err = ReadResponse(sh, &responseCode, response);
  1311.             if (err != noErr) goto exit;
  1312.             (*doOneResponse)(responseCode, response);
  1313.         }
  1314.     }
  1315.     MyHSetState(commands, state);
  1316.     return noErr;
  1317.     
  1318. exit:
  1319.  
  1320.     DoTCPRelease(sh);
  1321.     DisposeStreamBuffer(sh);
  1322.     MyHSetState(commands, state);
  1323.     return err;
  1324. }
  1325.  
  1326.  
  1327.  
  1328. /*----------------------------------------------------------------------------
  1329.     NetSendText 
  1330.     
  1331.     Send command text to a server.
  1332.     
  1333.     Entry:    stream = handle to stream.
  1334.             text = handle to CR-terminated ASCII text lines.
  1335.                 Warning: the memory block is modified by the function.
  1336.                 The memory block must be unlocked and nonpurgeable.
  1337.     
  1338.     Exit:    function result = system error result code.
  1339.     
  1340.     CR line terminators are translated to CRLF. A terminating "." on a line
  1341.     by itself is sent. The caller should not include this terminating line
  1342.     in the "text" block. Leading "." characters on lines are mapped to "..".
  1343.     
  1344.     If the text block does not have a terminating CR at the end of the last
  1345.     line, one is added.
  1346. ----------------------------------------------------------------------------*/
  1347.  
  1348. OSErr NetSendText (NetStreamHandle stream, Handle text)
  1349. {
  1350.     TStreamHandle sh;
  1351.     TStreamPtr s;
  1352.     OSErr err = noErr;
  1353.     char *p;
  1354.     long textLen;
  1355.     unsigned short len;
  1356.     char state;
  1357.     
  1358.     state = MyHGetState(text);
  1359.  
  1360.     err = MungeOut(text, true);
  1361.     if (err != noErr) goto exit;
  1362.     textLen = MyGetHandleSize(text);
  1363.     
  1364.     sh = (TStreamHandle)stream;
  1365.     s = (TStreamPtr)StripAddress(*sh);
  1366.     s->wds[1].length = 0;
  1367.     s->wds[1].ptr = nil;
  1368.     MyHLock(text);
  1369.     p = *text;
  1370.     while (textLen > 0) {
  1371.         len = textLen > 4000 ? 4000 : textLen;
  1372.         s->wds[0].length = len;
  1373.         s->wds[0].ptr = StripAddress(p);
  1374.         err = DoTCPSend(sh);
  1375.         if (err != noErr) goto exit;
  1376.         p += len;
  1377.         textLen -= len;
  1378.     }
  1379.     MyHSetState(text, state);
  1380.     return noErr;
  1381.     
  1382. exit:
  1383.  
  1384.     DoTCPRelease(sh);
  1385.     DisposeStreamBuffer(sh);
  1386.     MyHSetState(text, state);
  1387.     return err;
  1388. }
  1389.  
  1390.  
  1391.  
  1392. /*----------------------------------------------------------------------------
  1393.     NetGetText 
  1394.     
  1395.     Get response text from a server.
  1396.     
  1397.     Entry:    stream = handle to stream.
  1398.     
  1399.     Exit:    function result = system error result code.
  1400.             *text = handle to received ASCII text lines. Each line is
  1401.                 terminated with a CR.
  1402.             
  1403.     CRLF line terminators are translated to CR. The server must send a 
  1404.     terminating "." on a line by itself. This terminating line is not 
  1405.     included in the "text" handle returned to the caller. Leading
  1406.     ".." characters on lines are mapped to ".".
  1407. ----------------------------------------------------------------------------*/
  1408.  
  1409. OSErr NetGetText (NetStreamHandle stream, Handle *text)
  1410. {
  1411.     Boolean truncated;
  1412.  
  1413.     return GetText(stream, nil, text, &truncated);
  1414. }
  1415.  
  1416.  
  1417.  
  1418. /*----------------------------------------------------------------------------
  1419.     NetGetTextWithTruncation
  1420.     
  1421.     Get response text from a server with possible truncation.
  1422.     
  1423.     Entry:    stream = handle to stream.
  1424.             truncateFunction = pointer to truncate function, or nil if none.
  1425.     
  1426.     Exit:    function result = system error result code.
  1427.             *text = handle to received ASCII text lines. Each line is
  1428.                 terminated with a CR.
  1429.             *truncated = true if text truncated.
  1430.             
  1431.     CRLF line terminators are translated to CR. The server must send a 
  1432.     terminating "." on a line by itself. This terminating line is not 
  1433.     included in the "text" handle returned to the caller. Leading
  1434.     ".." characters on lines are mapped to ".".
  1435.     
  1436.     The truncation function is called every time a new block of text is
  1437.     received from the server. This function must be declared as follows:
  1438.     
  1439.     Boolean TruncateFunction (Handle t, long tLen, long *pos)
  1440.     
  1441.     Entry:    t = handle to raw text received from server so far.
  1442.             tLen = length of text received from server so far.
  1443.             *pos = saved position in text (0 on first call).
  1444.             
  1445.     Exit:    function result = true if text should be truncated.
  1446.             *pos = updated saved position in text if no truncation,
  1447.                 length of truncated text if truncated.
  1448.                 
  1449.     If the truncate function returns true, NetGetTextWithTruncation aborts
  1450.     the stream and returns to the caller immediately.
  1451.     
  1452.     The text passed to the truncate function is raw. It contains CRLF line
  1453.     terminators and doubled leading ".." characters. The text returned to 
  1454.     NetGetTextWithTruncation's caller, however, is processed as indicated
  1455.     above.
  1456. ----------------------------------------------------------------------------*/
  1457.  
  1458. OSErr NetGetTextWithTruncation (NetStreamHandle stream, NetTruncateFunction truncateFunction,
  1459.     Handle *text, Boolean *truncated)
  1460. {
  1461.     return GetText(stream, truncateFunction, text, truncated);
  1462. }
  1463.  
  1464.  
  1465.  
  1466. /*----------------------------------------------------------------------------
  1467.     NetGetChunkyText 
  1468.     
  1469.     Get response text from a server in chunks.
  1470.     
  1471.     Entry:    stream = handle to stream.
  1472.             chunkFunction = pointer to chunk processing function
  1473.     
  1474.     Exit:    function result = system error result code.
  1475.             *aborted = true if the operation was aborted.
  1476.     
  1477.     The chunk processing function is called as text is received from the
  1478.     server. It must be declared as follows:
  1479.     
  1480.     OSErr ChunkFunction (Ptr t, long tLen, Boolean *abort)
  1481.     
  1482.     Entry:    t = pointer to chunk.
  1483.             tLen = length of chunk.
  1484.             
  1485.     Exit:    function result = error code.
  1486.             *abort = true to abort the operation.
  1487.     
  1488.     The text passed to the chunk function is raw. It contains CRLF line 
  1489.     terminators and doubled leading ".." characters. The chunks do not
  1490.     necessarily end on CRLF line boundaries. The terminating "." character
  1491.     on a line by itself is *not* passed to the chunk function, however.
  1492. ----------------------------------------------------------------------------*/
  1493.  
  1494. OSErr NetGetChunkyText (NetStreamHandle stream, NetChunkFunction chunkFunction,
  1495.     Boolean *aborted)
  1496. {
  1497.     TStreamHandle sh;
  1498.     TStreamPtr s;
  1499.     OSErr err = noErr;
  1500.     Ptr t = nil;
  1501.     long tLen = 0, tAllocated;
  1502.     unsigned short len;
  1503.     char *tEnd;
  1504.     Boolean abort = false;
  1505.     
  1506.     sh = (TStreamHandle)stream;
  1507.     s = (TStreamPtr)StripAddress(*sh);
  1508.     tLen = s->in - s->out;
  1509.     tAllocated = tLen + 4005;
  1510.     err = MyNewPtr(tAllocated, &t);
  1511.     if (err != noErr) goto exit;
  1512.     if (tLen > 0) BlockMoveData(s->out, t, tLen);
  1513.     s->in = s->out = s->inputBuf;
  1514.     
  1515.     while (true) {
  1516.         if (tLen >= 3) {
  1517.             tEnd = t + tLen;
  1518.             if (*(tEnd-3) == '.' && *(tEnd-2) == CR && *(tEnd-1) == LF) {
  1519.                 if (tLen == 3) break;
  1520.                 if (tLen >= 5 && *(tEnd-5) == CR && *(tEnd-4) == LF) break;
  1521.             }
  1522.         }
  1523.         len = 4000;
  1524.         err = DoTCPRcv(sh, t + tLen, &len);
  1525.         if (err != noErr) goto exit;
  1526.         tLen += len;
  1527.         if (tLen >= 5) {
  1528.             err = (*chunkFunction)(t, tLen - 5, &abort);
  1529.             if (err != noErr) goto exit;
  1530.             if (abort) break;
  1531.             BlockMoveData(t + tLen - 5, t, 5);
  1532.             tLen = 5;
  1533.         }
  1534.     }
  1535.     
  1536.     if (abort) {
  1537.         DoTCPRelease(sh);
  1538.         DisposeStreamBuffer(sh);
  1539.         *aborted = true;
  1540.     } else {
  1541.         tLen -= 3;
  1542.         if (tLen > 0) {
  1543.             err = (*chunkFunction)(t, tLen, &abort);
  1544.             if (err != noErr) goto exit;
  1545.         }
  1546.         *aborted = false;
  1547.     }
  1548.     
  1549.     MyDisposePtr(t);
  1550.  
  1551.     return noErr;
  1552.     
  1553. exit:
  1554.  
  1555.     DoTCPRelease(sh);
  1556.     DisposeStreamBuffer(sh);
  1557.     MyDisposePtr(t);
  1558.     return err;
  1559. }
  1560.  
  1561.  
  1562.  
  1563. /*----------------------------------------------------------------------------
  1564.     NetFTPDataOpen 
  1565.     
  1566.     Open an FTP data stream.
  1567.     
  1568.     Exit:    function result = system error result code.
  1569.             *port = assigned unused local port number.
  1570.             *stream = pointer to opened FTP data stream.
  1571.     
  1572.     This function returns immediately. It should be followed by a call to
  1573.     NetFTPDataWaitForConnection to wait for the FTP server to open its end of 
  1574.     the data stream connection.
  1575. ----------------------------------------------------------------------------*/
  1576.  
  1577. OSErr NetFTPDataOpen (unsigned short *port, NetStreamHandle *stream)
  1578. {
  1579.     TStreamHandle sh;
  1580.     TStreamPtr s;
  1581.     OSErr err = noErr;
  1582.  
  1583.     err = NewStreamBuffer(&sh);
  1584.     if (err != noErr) return err;
  1585.     s = (TStreamPtr)StripAddress(*sh);
  1586.     s->in = s->out = s->inputBuf;
  1587.     s->myA5 = SetCurrentA5();
  1588.     err = DoTCPCreate(sh);
  1589.     if (err != noErr) goto exit2;
  1590.     err = DoTCPPassiveOpen(sh, port);
  1591.     if (err != noErr) goto exit1;
  1592.     *stream = (NetStreamHandle)sh;
  1593.     return noErr;
  1594.     
  1595. exit1:
  1596.  
  1597.     DoTCPRelease(sh);
  1598.     
  1599. exit2:
  1600.     
  1601.     MyDisposeHandle(sh);
  1602.     return err;
  1603. }
  1604.  
  1605.  
  1606.  
  1607. /*----------------------------------------------------------------------------
  1608.     NetFTPDataClose
  1609.     
  1610.     Close an FTP data stream.
  1611.     
  1612.     Entry:    stream = handle to FTP data stream.
  1613.     
  1614.     Exit:    function result = system error result code (always noErr).
  1615. ----------------------------------------------------------------------------*/
  1616.  
  1617. OSErr NetFTPDataClose (NetStreamHandle stream)
  1618. {
  1619.     TStreamHandle sh;
  1620.     TStreamPtr s;
  1621.     OSErr err = noErr;
  1622.     
  1623.     sh = (TStreamHandle)stream;
  1624.     s = (TStreamPtr)StripAddress(*sh);
  1625.     
  1626.     /* Link the stream into the queue of closing streams. */
  1627.  
  1628.     s->finished = false;
  1629.     s->next = gCloseQueue;
  1630.     gCloseQueue = sh;
  1631.     
  1632.     /* Start an asynchronous close operation, chained to our 
  1633.        completion routine. */
  1634.  
  1635.     InitTCPBlock(&s->pBlock, TCPClose);
  1636.     s->pBlock.ioCompletion = 
  1637.         (TCPIOCompletionProc)gCloseStreamCompletionRoutineUPP;
  1638.     s->pBlock.tcpStream = s->tcpStream;
  1639.     err = PBControlAsync((ParmBlkPtr)&s->pBlock);
  1640.     if (err != noErr) s->finished = true;
  1641.     return noErr;
  1642. }
  1643.  
  1644.  
  1645.  
  1646. /*----------------------------------------------------------------------------
  1647.     NetFTPDataWaitForConnection 
  1648.     
  1649.     Wait for an FTP server to open its end of an FTP data stream.
  1650.     
  1651.     Entry:    stream = handle to FTP data stream.
  1652.     
  1653.     Exit:    function result = system error result code.
  1654. ----------------------------------------------------------------------------*/
  1655.  
  1656. OSErr NetFTPDataWaitForConnection (NetStreamHandle stream)
  1657. {
  1658.     TStreamHandle sh;
  1659.     TStreamPtr s;
  1660.     OSErr err = noErr;
  1661.     
  1662.     sh = (TStreamHandle)stream;
  1663.     s = (TStreamPtr)StripAddress(*sh);
  1664.     while (s->pBlock.ioResult > 0) {
  1665.         err = (*gGiveTime)(true);
  1666.         if (err != noErr) goto exit;
  1667.     }
  1668.     err = s->pBlock.ioResult;
  1669.     if (err != noErr) goto exit;
  1670.     return noErr;
  1671.         
  1672. exit:
  1673.  
  1674.     DoTCPRelease(sh);
  1675.     MyDisposeHandle(sh);
  1676.     return err;
  1677. }
  1678.  
  1679.  
  1680.  
  1681. /*----------------------------------------------------------------------------
  1682.     NetGetFTPTextFile 
  1683.     
  1684.     Get a text file from an FTP server.
  1685.     
  1686.     Entry:    stream = handle to FTP data stream.
  1687.     
  1688.     Exit:    function result = system error result code.
  1689.             *text = handle to received text. Each line is terminated
  1690.                 with a CR.
  1691.             
  1692.     CRLF line terminators are mapped to CR.
  1693. ----------------------------------------------------------------------------*/
  1694.  
  1695. OSErr NetGetFTPTextFile (NetStreamHandle stream, Handle *text)
  1696. {
  1697.     TStreamHandle sh;
  1698.     OSErr err = noErr;
  1699.     Handle t = nil;
  1700.     long tLen = 0;
  1701.     long tAllocated = 4000;
  1702.     long numFree;
  1703.     unsigned short len;
  1704.     
  1705.     sh = (TStreamHandle)stream;
  1706.     err = MyNewHandle(tAllocated, &t);
  1707.     if (err != noErr) goto exit;
  1708.     
  1709.     while (true) {
  1710.         numFree = tAllocated - tLen;
  1711.         if (numFree <= 4000) {
  1712.             tAllocated += 4000;
  1713.             err = MySetHandleSize(t, tAllocated);
  1714.             if (err != noErr) goto exit;
  1715.         }
  1716.         len = 4000;
  1717.         MyHLock(t);
  1718.         err = DoTCPRcv(sh, *t + tLen, &len);
  1719.         MyHUnlock(t);
  1720.         if (err == userCanceledErr) goto exit;
  1721.         if (err != noErr) break; 
  1722.         tLen += len; 
  1723.     }
  1724.     
  1725.     err = MungeIn(t, tLen, false);
  1726.     if (err != noErr) goto exit;
  1727.     *text = t;
  1728.     return noErr;
  1729.     
  1730. exit:
  1731.  
  1732.     DoTCPRelease(sh);
  1733.     MyDisposeHandle(sh);
  1734.     MyDisposeHandle(t);
  1735.     return err;
  1736. }
  1737.  
  1738.  
  1739.  
  1740. /*----------------------------------------------------------------------------
  1741.     NetSendFTPTextFile 
  1742.     
  1743.     Send a text file to an FTP server.
  1744.     
  1745.     Entry:    stream = handle to FTP data stream.
  1746.             text = handle to text. Each line must be terminated by CR.
  1747.                 Warning: the memory block is modified by the function.
  1748.                 The memory block must be unlocked and nonpurgeable.
  1749.     
  1750.     Exit:    function result = system error result code.
  1751.  
  1752.     CR line terminators are translated to CRLF.
  1753. ----------------------------------------------------------------------------*/
  1754.  
  1755. OSErr NetSendFTPTextFile (NetStreamHandle stream, Handle text)
  1756. {
  1757.     TStreamHandle sh;
  1758.     TStreamPtr s;
  1759.     OSErr err = noErr;
  1760.     char *p;
  1761.     long textLen;
  1762.     unsigned short len;
  1763.     char state;
  1764.     
  1765.     state = MyHGetState(text);
  1766.  
  1767.     err = MungeOut(text, false);
  1768.     if (err != noErr) goto exit;
  1769.     textLen = MyGetHandleSize(text);
  1770.     
  1771.     sh = (TStreamHandle)stream;
  1772.     s = (TStreamPtr)StripAddress(*sh);
  1773.     s->wds[1].length = 0;
  1774.     s->wds[1].ptr = nil;
  1775.     MyHLock(text);
  1776.     p = *text;
  1777.     while (textLen > 0) {
  1778.         len = textLen > 4000 ? 4000 : textLen;
  1779.         s->wds[0].length = len;
  1780.         s->wds[0].ptr = StripAddress(p);
  1781.         err = DoTCPSend(sh);
  1782.         if (err != noErr) goto exit;
  1783.         p += len;
  1784.         textLen -= len;
  1785.     }
  1786.     MyHSetState(text, state);
  1787.     return noErr;
  1788.     
  1789. exit:
  1790.  
  1791.     DoTCPRelease(sh);
  1792.     MyDisposeHandle(sh);
  1793.     MyHSetState(text, state);
  1794.     return err;
  1795. }
  1796.  
  1797.  
  1798.  
  1799. /*----------------------------------------------------------------------------
  1800.     NetGetMyAddr 
  1801.     
  1802.     Get this Mac's IP address.
  1803.     
  1804.     Exit:    function result = system error result code.
  1805.             *addr = the IP address of this Mac.
  1806. ----------------------------------------------------------------------------*/
  1807.  
  1808. OSErr NetGetMyAddr (unsigned long *addr)
  1809. {
  1810.     struct GetAddrParamBlock pBlock;
  1811.     OSErr err = noErr;
  1812.     
  1813.     memset(&pBlock, 0, sizeof(pBlock));
  1814.     pBlock.ioResult = 1;
  1815.     pBlock.csCode = ipctlGetAddr;
  1816.     pBlock.ioCRefNum = gRefNum;
  1817.     PBControlAsync((ParmBlkPtr)&pBlock);
  1818.     while (pBlock.ioResult > 0) err = (*gGiveTime)(true);
  1819.     *addr = pBlock.ourAddress;
  1820.     if (err == noErr) err = pBlock.ioResult;
  1821.     return err;
  1822. }
  1823.  
  1824.  
  1825.  
  1826. /*----------------------------------------------------------------------------
  1827.     NetGetMyAddrStr 
  1828.     
  1829.     Get this Mac's IP address as a dotted-decimal string
  1830.  
  1831.     Exit:    function result = system error result code.
  1832.             name = this Mac's IP address, as a C-format string.
  1833.                 You must allocate at least 16 bytes for this string.
  1834.                 The returned string has max length 15.
  1835. ----------------------------------------------------------------------------*/
  1836.     
  1837. OSErr NetGetMyAddrStr (char *addrStr)
  1838. {
  1839.     unsigned long addr;
  1840.     OSErr err = noErr;
  1841.     static char theAddrStr[16];
  1842.     static Boolean gotIt=false;
  1843.     
  1844.     if (!gotIt) {
  1845.         err = NetGetMyAddr(&addr);
  1846.         if (err != noErr) return err;
  1847.         err = OpenResolver(nil);
  1848.         if (err != noErr) return err;
  1849.         err = AddrToStr(addr, theAddrStr);
  1850.         CloseResolver();
  1851.         if (err != noErr) return err;
  1852.         gotIt = true;
  1853.     }
  1854.     strcpy(addrStr, theAddrStr);
  1855.     return noErr;
  1856. }
  1857.  
  1858.  
  1859.  
  1860. /*----------------------------------------------------------------------------
  1861.     NetGetMyName 
  1862.     
  1863.     Get this Mac's domain name, if any.
  1864.     
  1865.     Exit:    function result = system error result code.
  1866.             name = domain name of this Mac, as a C-format string.
  1867. ----------------------------------------------------------------------------*/
  1868.  
  1869. OSErr NetGetMyName (CStr255 name)
  1870. {
  1871.     unsigned long addr;
  1872.     short len;
  1873.     static OSErr err = noErr;
  1874.     static Boolean gotIt=false;
  1875.     static CStr255 theName;
  1876.  
  1877.     if (!gotIt) {
  1878.         err = NetGetMyAddr(&addr);
  1879.         if (err != noErr) return err;
  1880.         err = NetAddrToName(addr, theName);
  1881.         gotIt = true;
  1882.         len = strlen(theName);
  1883.         if (theName[len-1] == '.') theName[len-1] = 0;
  1884.     }
  1885.     if (err != noErr) return err;
  1886.     strcpy(name, theName);
  1887.     return noErr;
  1888. }
  1889.  
  1890.  
  1891.  
  1892. /*----------------------------------------------------------------------------
  1893.     NetNameToAddr 
  1894.     
  1895.     Translate a domain name to an IP address.
  1896.     
  1897.     Entry:     name = C-format domain name string, optionally followed by a
  1898.                 comma and port number.
  1899.             defaultPort = default port number
  1900.     
  1901.     Exit:    function result = system error result code.
  1902.             *addr = IP address.
  1903.             *port = port number.
  1904. ----------------------------------------------------------------------------*/
  1905.  
  1906. OSErr NetNameToAddr (char *name, unsigned short defaultPort, 
  1907.     unsigned long *addr, unsigned short *port)
  1908. {
  1909.     OSErr err = noErr;
  1910.     short i;
  1911.     static struct {
  1912.         CStr255 domainName;
  1913.         unsigned long addr;
  1914.     } cache[10];
  1915.     static short numCache = 0;
  1916.     struct hostInfo hInfo;
  1917.     Boolean done = false;
  1918.     Boolean canceled = false;
  1919.     CStr255 domainName;
  1920.     char *p, *q;
  1921.     
  1922.     p = name;
  1923.     q = domainName;
  1924.     while (*p !=0 && *p != ',') *q++ = *p++;
  1925.     *q = 0;
  1926.     if (*p == ',') {
  1927.         p++;
  1928.         *port = atoi(p);
  1929.     } else {
  1930.         *port = defaultPort;
  1931.     }
  1932.     
  1933.     for (i=0; i<numCache; i++) {
  1934.         if (strcmp(domainName, cache[i].domainName) == 0) {
  1935.             *addr = cache[i].addr;
  1936.             return noErr;
  1937.         }
  1938.     }
  1939.  
  1940.     err = OpenResolver(nil);
  1941.     if (err != noErr) return err;
  1942.     memset(&hInfo, 0, sizeof(hInfo));
  1943.     gDNROperationInProgress = true;
  1944.     err = StrToAddr(domainName, &hInfo, gDNRResultProcUPP, (char*)&done);
  1945.     if (err == cacheFault) {
  1946.         while (!done) canceled = (*gGiveTime)(true) == userCanceledErr || canceled;
  1947.         err = hInfo.rtnCode;
  1948.     }
  1949.     gDNROperationInProgress = false;
  1950.     (*gGiveTime)(false);
  1951.     CloseResolver();
  1952.     if (canceled) return userCanceledErr;
  1953.     if (err != noErr) return err;
  1954.     *addr = hInfo.addr[0];
  1955.     
  1956.     if (numCache < 10) {
  1957.         strcpy(cache[numCache].domainName, domainName);
  1958.         cache[numCache].addr = *addr;
  1959.         numCache++;
  1960.     }
  1961.     return noErr;
  1962. }
  1963.  
  1964.  
  1965.  
  1966. /*----------------------------------------------------------------------------
  1967.     NetAddrToName 
  1968.     
  1969.     Translate an IP address to a domain name.
  1970.     
  1971.     Entry:    addr = IP address.
  1972.     
  1973.     Exit:    function result = system error result code.
  1974.             name = domain name, as a C-format string.
  1975. ----------------------------------------------------------------------------*/
  1976.  
  1977. OSErr NetAddrToName (unsigned long addr, CStr255 name)
  1978. {
  1979.     struct hostInfo hInfo;
  1980.     OSErr err = noErr;
  1981.     Boolean done=false;
  1982.     Boolean canceled=false;
  1983.     
  1984.     err = OpenResolver(nil);
  1985.     if (err != noErr) return err;
  1986.     memset(&hInfo, 0, sizeof(hInfo));
  1987.     gDNROperationInProgress = true;
  1988.     err = AddrToName(addr, &hInfo, gDNRResultProcUPP, (char*)&done);
  1989.     if (err == cacheFault) {
  1990.         while (!done) canceled = (*gGiveTime)(true) == userCanceledErr || canceled;
  1991.         err = hInfo.rtnCode;
  1992.     }
  1993.     gDNROperationInProgress = false;
  1994.     (*gGiveTime)(false);
  1995.     CloseResolver();
  1996.     if (canceled) return userCanceledErr;
  1997.     if (err != noErr) return err;
  1998.     hInfo.cname[254] = 0;
  1999.     strcpy(name, hInfo.cname);
  2000.     return noErr;
  2001. }
  2002.  
  2003.  
  2004.  
  2005.  
  2006. /*----------------------------------------------------------------------------
  2007.     NetGiveTime 
  2008.     
  2009.     Give time to background processes and check for user cancel operations.
  2010.     
  2011.     Entry:    waiting = true if waiting for MacTCP or something else. 
  2012.     
  2013.     Exit:    function result = error code.
  2014. ----------------------------------------------------------------------------*/
  2015.  
  2016. OSErr NetGiveTime (Boolean waiting)
  2017. {
  2018.     return (*gGiveTime)(waiting);
  2019. }
  2020.  
  2021.  
  2022.  
  2023. /*----------------------------------------------------------------------------
  2024.     NetDNROperationInProgress 
  2025.     
  2026.     Find out whether there is a DNR operation in progress.
  2027.     
  2028.     Exit:    function result = true if DNR operation in progress.
  2029. ----------------------------------------------------------------------------*/
  2030.  
  2031. Boolean NetDNROperationInProgress (void)
  2032. {
  2033.     return gDNROperationInProgress;
  2034. }
  2035.  
  2036.  
  2037.  
  2038. /*----------------------------------------------------------------------------
  2039.     net_InitUPP
  2040.     
  2041.     Initialize UPPs.
  2042. ----------------------------------------------------------------------------*/
  2043.  
  2044. void net_InitUPP (void)
  2045. {
  2046.     gDNRResultProcUPP = NewResultProc(DNRResultProc);
  2047.     gCloseStreamCompletionRoutineUPP = NewTCPIOCompletionProc(CloseStreamCompletionRoutine);
  2048. }